use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
use idgenerator::IdInstance;
use ring::digest::{Context, SHA512};
use ring::{digest, rand};
use ring::rand::SecureRandom;
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, DeleteResult, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, Set};
use crate::{DB, db_conn};
use crate::entity::{sys_user, sys_user::Entity as User};
use crate::models::page::{Page, PageParam};
use crate::models::sys_user as dto;
use crate::models::sys_user::ResultUser;

pub struct SysUserService;

const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
fn generate_salt() -> [u8; CREDENTIAL_LEN] {
    // 生成一个CREDENTIAL_LEN字节的随机盐值
    let rng = rand::SystemRandom::new();
    let mut salt = [0u8; CREDENTIAL_LEN];
    rng.fill(&mut salt).unwrap();
    salt
}

fn hash_password(password: &str, salt: &[u8; CREDENTIAL_LEN]) -> Vec<u8> {
    // 创建一个SHA-512的哈希上下文
    let mut context = Context::new(&SHA512);

    // 将密码和盐值输入到哈希上下文
    context.update(password.as_bytes());
    context.update(salt);

    // 获取最终的哈希值
    let hashed_password = context.finish();
    hashed_password.as_ref().to_vec()
}

impl SysUserService {
    pub async fn insert(
        model: dto::UserCreateCmd,
    ) -> Result<String, DbErr> {
        let db = DB.get_or_init(db_conn).await;

        let start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
        let salt = generate_salt();
        let password_hash = hex::encode(hash_password(&model.password, &salt));
        let end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis();
        tracing::info!("密码加密耗时 >>>>>>> {}ms", end - start);

        let user = sys_user::ActiveModel {
            id: Set(IdInstance::next_id().to_string()),
            username: Set(model.username.to_owned()),
            password: Set(password_hash.to_owned()),
            gender: Set(model.gender.to_owned()),
            email: Set(model.email.to_owned()),
            salt: Set(hex::encode(salt)),
        };
        let res = user.insert(db).await?;
        tracing::info!("res >>>> {:?}", res);
        Ok(res.id)
    }

    pub async fn update(
        model: dto::UserUpdateCmd,
    ) -> Result<String, DbErr> {
        let db = DB.get_or_init(db_conn).await;
        sys_user::ActiveModel {
            id: Set(model.id.to_owned()),
            username: Set(model.username.to_owned()),
            gender: Set(model.gender.to_owned()),
            email: Set(model.email.to_owned()),
            ..Default::default()
        }.update(db).await?;

        Ok("更新成功".to_string())
    }

    pub async fn delete(id: String) -> Result<DeleteResult, DbErr> {
        let db = DB.get_or_init(db_conn).await;
        let user: sys_user::ActiveModel = User::find_by_id(id)
            .one(db)
            .await?
            .ok_or(DbErr::Custom("未查询到用户！".to_owned()))
            .map(Into::into)?;

        user.delete(db).await
    }

    pub async fn delete_all() -> Result<DeleteResult, DbErr> {
        let db = DB.get_or_init(db_conn).await;
        User::delete_many().exec(db).await
    }

    pub async fn page_user(page_param: PageParam, params: HashMap<String, String>) -> Result<Page<ResultUser>, DbErr> {
        let db = DB.get_or_init(db_conn).await;
        let page_no = page_param.page_no.unwrap_or(1);
        let page_size = page_param.page_size.unwrap_or(10);

        // 生成查询条件
        let mut select = User::find();

        if let Some(x) =  params.get("username") {
            if !x.is_empty() {
                select = select.filter(sys_user::Column::Username.contains(x));
            }
        }

        // 查询分页数据
        let total = select.clone().count(db).await?;
        let paginator = select
            .order_by_asc(sys_user::Column::Id)
            .into_model::<ResultUser>()
            .paginate(db, page_size);
        let total_page = paginator.num_pages().await?;
        let records = paginator.fetch_page(page_no - 1).await?;

        // 返回结果
        Ok(Page {
            page_no,
            page_size,
            total,
            total_page,
            records
        })
    }
}